#ifndef TIMER_H
#define TIMER_H

#include <functional>
#include <bsp.h>
#include <cmsis_os.h>
#include <clock.h>
#include <units.h>
#include <core.h>

namespace os {
/**
 * @brief A class designed to enforce strict timing control for thread tasks.
 *
 * @details FrequencyController provides a simple mechanism to execute tasks at
 * precise intervals, independent of the task's execution time. Unlike simple
 * sleep or delay functions that operate relative to when a task finishes, this
 * class schedules tasks based on absolute time points. This ensures tasks are
 * run at consistent intervals, making it suitable for applications requiring
 * strict timing precision such as real-time data processing or simulation
 * updates.
 */
class FrequencyGuard {
    units::time::millisecond_t begin_{};
    units::time::millisecond_t end_{};
    units::time::millisecond_t time_{};
    explicit FrequencyGuard(units::time::millisecond_t const t) : time_{t} { begin_ = getDWTTime_u32(); }
    explicit FrequencyGuard(units::frequency::hertz_t const h) : time_{1 / h} { begin_ = getDWTTime_u32(); }
    ~FrequencyGuard() {
        end_ = getDWTTime_u32();
        if (end_ - begin_ < time_) {
            sleep(time_ - end_ + begin_);
        }
    }
};

class Timer {
   public:
    enum class ReloadOption : bool { kNoAutoReload = 0U, kAutoReload = 1U };
    enum class StartOption : bool { kNoAutoStart = 0U, kAutoStart = 1U };

    using CallbackFuncType = std::function<void(uint32_t)>;

    Timer(units::time::millisecond_t const reschedule_ticks, ReloadOption auto_reload, StartOption auto_start,
          CallbackFuncType const& callback)
        : callback_fn(std::move(callback)) {
        hTimer = xTimerCreate("", reschedule_ticks.to<uint32_t>(), static_cast<bool>(auto_reload), this, FnTypeWrapper);

        os_assert(hTimer, "create timer failed");

        if (static_cast<bool>(auto_start)) {
            UNUSED() this->start();
        }
    }

    Timer() = delete;
    Timer(Timer const&) = delete;
    Timer(Timer&&) = delete;
    Timer& operator=(Timer const&) = delete;

    ~Timer() { xTimerDelete(hTimer, portMAX_DELAY); }

    bool start() const { return xTimerStart(hTimer, portMAX_DELAY); }

    bool startFromISR() const { return xTimerStartFromISR(hTimer, NULL); }

    bool stop() const { return xTimerStop(hTimer, portMAX_DELAY); }

    bool stopFromISR() const { return xTimerStopFromISR(hTimer, NULL); }

    bool change(units::time::millisecond_t reschedule_ticks) const {
        return xTimerChangePeriod(hTimer, reschedule_ticks.to<uint32_t>(), portMAX_DELAY);
    }

    bool startSafe() const { return (xPortIsInsideInterrupt()) ? startFromISR() : start(); }

    bool stopSafe() const { return (xPortIsInsideInterrupt()) ? stopFromISR() : stop(); }

    bool changeSafe(units::time::millisecond_t const reschedule_ticks) const {
        if (xPortIsInsideInterrupt()) {
            return xTimerChangePeriodFromISR(hTimer, reschedule_ticks.to<uint32_t>(), NULL);
        }
        return change(reschedule_ticks);
    }

    TimerHandle_t get_hTimer() const { return hTimer; }

   private:
    static void FnTypeWrapper(TimerHandle_t TimerPtr) {
        auto const _hTimer = static_cast<Timer*>(pvTimerGetTimerID(TimerPtr));
        if (_hTimer && _hTimer->callback_fn) {
            _hTimer->callback_fn(_hTimer->param);
        }
    }

    TimerHandle_t hTimer;
    CallbackFuncType callback_fn;
    uint32_t param = 0;
};
}  // namespace os

#if BSP_HAS_HARD_TIMER == 1
#include <bsp/clock.h>
/**
 * Class Hard Timer
 */
namespace os {
class HardTimer {
   public:
    using Channel = ::bsp::detail::HardTimerChannel;

    HardTimer() = delete;

    static void Init();

    static void Start(Channel channel, units::time::microsecond_t time, std::function<void()> const& call_back);

    static void Delay(Channel channel, units::time::microsecond_t time);
};
}  // namespace os
#endif

#endif  // TIMER_H